/*
 * Copyright 2011 Clean GWT
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
 * in compliance with the License. You may obtain a copy of the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software distributed under the License
 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing permissions and limitations under
 * the License.
 */
package com.cleangwt.uibinder.rebind;

import java.util.List;

import com.cleangwt.uibinder.client.RequiresWebTemplate;
import com.cleangwt.uibinder.client.TemplatePanel;
import com.cleangwt.uibinder.client.UiField;
import com.cleangwt.uibinder.client.UiWebTemplate;
import com.google.gwt.core.ext.UnableToCompleteException;
import com.google.gwt.core.ext.typeinfo.JClassType;
import com.google.gwt.core.ext.typeinfo.JField;
import com.google.gwt.core.ext.typeinfo.JType;
import com.google.gwt.http.client.Request;
import com.google.gwt.http.client.RequestBuilder;
import com.google.gwt.http.client.RequestCallback;
import com.google.gwt.http.client.RequestException;
import com.google.gwt.http.client.Response;
import com.google.gwt.user.rebind.ClassSourceFileComposerFactory;
import com.google.gwt.user.rebind.SourceWriter;

/**
 * A writer for generating implementation methods of {@link RequiresWebTemplate} .
 * 
 * @author Jess
 * @version 1.0
 * @since 1.0
 */
public class WebTemplateRebindWriter {
  private final JClassType binderType;
  private List<JField> uiFields;

  public WebTemplateRebindWriter(JClassType binderType) {
    this.binderType = binderType;
    this.uiFields = TemplateRebindHelper.findUiFields(binderType);
  }

  /**
   * Adds imports.
   * 
   * @param composer the composer
   */
  public void addImports(ClassSourceFileComposerFactory composer) {
    composer.addImport(TemplatePanel.class.getCanonicalName());
    composer.addImport(RequestBuilder.class.getCanonicalName());
    composer.addImport(RequestCallback.class.getCanonicalName());
    composer.addImport(Request.class.getCanonicalName());
    composer.addImport(Response.class.getCanonicalName());
    composer.addImport(RequestException.class.getCanonicalName());
    for (JField field : uiFields) {
      composer.addImport(field.getType().getQualifiedSourceName());
    }
  }

  /**
   * Generates implementation methods.
   * 
   * @param sw the source writer
   * @param className the output class name
   * @param binderType the binder type
   * @throws UnableToCompleteException
   */
  public void run(SourceWriter sw, String className) throws UnableToCompleteException {
    // if the binder class does not implements the on-load method, create
    // one
    if (binderType.findMethod("onWebTemplateLoad", new JType[] {}) == null) {
      sw.println();
      sw.println("public void onWebTemplateLoad(){}");
    }
    createCreateTemplate(sw, className, binderType);
  }

  /**
   * Creates the {@link RequiresWebTemplate#createTemplate()}.
   * 
   * @param sw the source writer
   * @param binderType the binder type
   * @param uiFields the fields with {@link UiField}
   * @param handlerWriter the {@link EventHandlerWriter}
   * @throws UnableToCompleteException
   */
  private void createCreateTemplate(SourceWriter sw, String className, JClassType binderType)
      throws UnableToCompleteException {
    List<JField> uiFields = TemplateRebindHelper.findUiFields(binderType);
    sw.println();
    sw.println("public TemplatePanel createTemplate(){");
    sw.indent();
    sw.println("final TemplatePanel template = new TemplatePanel();");
    if (binderType.isAnnotationPresent(UiWebTemplate.class)) {
      String url = binderType.getAnnotation(UiWebTemplate.class).value();
      sw.println("RequestBuilder builder = new RequestBuilder(RequestBuilder.GET, \"%s\");", url);
    } else {
      // default url by 'module/jsp/{binderType}.jsp'
      sw.println(
          "RequestBuilder builder = new RequestBuilder(RequestBuilder.GET, com.google.gwt.core.client.GWT.getModuleName() + \"/jsp/%s.jsp\");",
          binderType.getSimpleSourceName());
    }
    sw.println("try {");
    sw.indent();
    sw.println("builder.sendRequest(null, new RequestCallback() {");
    sw.indent();
    sw.println("public void onError(Request request, Throwable exception) {");
    sw.indent();
    sw.println("throw new RuntimeException(exception);");
    sw.outdent();
    sw.println("}");
    sw.outdent();
    sw.println();
    sw.indent();
    sw.println("public void onResponseReceived(Request request,Response response) {");
    sw.indent();
    sw.println("template.clear();");
    sw.println("template.getElement().setInnerHTML(response.getText());");
    sw.println("if (response.getStatusCode() == Response.SC_OK) {");
    sw.indent();
    sw.indent();
    TemplateRebindHelper.createOrReplaceFields(sw, uiFields);
    sw.println("onWebTemplateLoad();");
    sw.outdent();
    sw.println("}");
    sw.outdent();
    sw.println("}");
    sw.outdent();
    sw.println("}");
    sw.outdent();
    sw.println(");");
    sw.outdent();
    sw.println("} catch (RequestException e) {");
    sw.indent();
    sw.println("throw new RuntimeException(e);");
    sw.outdent();
    sw.println("}");
    sw.println("return template;");
    sw.outdent();
    sw.println("}");
  }

}
